using System;
using System.Linq;
using System.Collections.Generic;

namespace Source.Common
{
    public static class ExtensionMethods
    {
        public static IEnumerable<T> TakeLastFrom<T>(this IEnumerable<T> enumerable, int startIndex)
        {
            GuardAgainstNullEnumberable(enumerable);
            GuardAgainstInvalidStartIndex(startIndex, enumerable);
            return GetSubset(enumerable, startIndex, enumerable.Count() - startIndex);
        }

        public static IEnumerable<T> Take<T>(this IEnumerable<T> enumerable, int startIndex, int count)
        {
            GuardAgainstNullEnumberable(enumerable);
            if (enumerable.Count() == 0) return enumerable;
            GuardAgainstInvalidStartIndex(startIndex, enumerable);
            return GetSubset(enumerable, startIndex, count);
        }

        public static bool ContainsAll<T>(this IEnumerable<T> enumerable, IEnumerable<T> elementsToContain)
        {
            return elementsToContain.Select(e => enumerable.Contains(e)).All(v => v);
        }

        private static List<T> GetSubset<T>(IEnumerable<T> enumerable, int startIndex, int count)
        {
            var list = new List<T>(count);

            int upperLimit = DetermineUpperLimit(startIndex, enumerable, count);

            for (int i = startIndex; i < upperLimit; i++)
            {
                list.Add(enumerable.ElementAt(i));
            }
            return list;
        }

        private static int DetermineUpperLimit<T>(int startIndex, IEnumerable<T> enumerable, int count)
        {
            int upperLimit = startIndex + count;
            if (upperLimit > enumerable.Count()) upperLimit = enumerable.Count();
            return upperLimit;
        }

        private static void GuardAgainstInvalidStartIndex<T>(int startIndex, IEnumerable<T> enumerable)
        {
            if (startIndex >= enumerable.Count() || startIndex < 0)
            {
                throw new InvalidOperationException("invalid startindex");
            }
        }

        private static void GuardAgainstNullEnumberable<T>(IEnumerable<T> enumerable)
        {
            if (enumerable == null)
            {
                throw new ArgumentNullException("enumerable");
            }
        }
    }
}